#include "stdafx.h"
 
namespace Melting
{
	ScriptParser::ScriptParser( UINT32 _uiMaxEntries )
	{
		maEntries.Allocate( _uiMaxEntries );
	}

	ScriptParser::~ScriptParser( void )
	{
		maEntries.Deallocate();
	}

	bool ScriptParser::Parse( const char* _strScript, UINT32 _uiScriptSize, char _cEntriesSeparator
							, char _cKeyValueSeparator, char _cCommentTag, bool _bIsValueOptionnal  )
	{
		bool bSuccess = true;

		#define GO_FORWARD_IF(_expr)	while( i < _uiScriptSize && (_expr) ) {++i;}
		#define SKIP_BLANK()        	GO_FORWARD_IF( ' ' == _strScript[ i ] )
		#define SKIP_ENTRY()        	GO_FORWARD_IF( _cEntriesSeparator != _strScript[ i ] );
		#define REACH_KEY_END()     	GO_FORWARD_IF(  ' ' != _strScript[ i ] \
													&& ( _cKeyValueSeparator != _strScript[ i ] && _cEntriesSeparator != _strScript[ i ] ) )
		#define REACH_VALUE_END()   	GO_FORWARD_IF(  ' ' != _strScript[ i ] && _cEntriesSeparator != _strScript[ i ] )

		UINT32 i = 0;
		while( i < _uiScriptSize )
		{
			// Process new entry ============================
			SKIP_BLANK();

			if( _cCommentTag == _strScript[ i ] )
			{
				SKIP_ENTRY();
			}
			// treat the entry if it is not a blank one / blank line
			else
			if ( _cEntriesSeparator != _strScript[ i ] )
			{

				// has the end of the line/entry been reached ? 
				bool bFoundKeyValueSeparator = true;

				// Found Key start, so now try to find key end
				UINT16 uiKeyStart = i;
				UINT16 uiKeyEnd;
				REACH_KEY_END();
				uiKeyEnd = i - 1;


				// Search for key value separator
				// if we are not on it yet...
				if( _cKeyValueSeparator != _strScript[ i ] )
				{
					// ... then go forward until there is something 'not blank'
					SKIP_BLANK();

					// and if the 'not blank' char is not the expected separator,
					// then consider it a script formatting error and abort
					if( _cKeyValueSeparator != _strScript[ i ] )
					{
						if( _bIsValueOptionnal && _cEntriesSeparator == _strScript[ i ] )
						{
							bFoundKeyValueSeparator = false; 
						}
						else
						{
							bFoundKeyValueSeparator = false;

							bSuccess = false;
							break;
						}
					}
				}
				// Search for Value start --------------------------------
				UINT16 uiValueStart;
				UINT16 uiValueEnd;

				if( bFoundKeyValueSeparator )
				{	
					//skip the key value separator 
					++i;

					SKIP_BLANK();

					// Found Value start, so now try to find Value end
					uiValueStart = i;
					REACH_VALUE_END();
					uiValueEnd = i - 1;
				}
				else
				{
					uiValueStart	= 1;	// make sure start > end !
					uiValueEnd		= 0;
				}

				// check that the key and value found are valid
				if(		uiKeyEnd <= uiKeyStart
					|| ( !_bIsValueOptionnal && uiValueEnd <= uiValueStart ) )
				{
					bSuccess = false;
					break;
				}

				// and register the new pair
				KeyValue& rKV = maEntries.TakeNext();
				rKV.muiKeySize   = uiKeyEnd - uiKeyStart + 1 ;
				rKV.muiValueSize = uiValueEnd - uiValueStart + 1 ;
				rKV.mxstrKey     = &_strScript[ uiKeyStart ];
				rKV.mxstrValue   = &_strScript[ uiValueStart ];


				// go forward the entries separator, in order to prepare for the next entry ===========
				// Search for key value separator
				// if we are not on it yet...
				if( _cEntriesSeparator != _strScript[ i ] )
				{
					SKIP_BLANK();

					// and if we have not reach the end of the script
					// and if the 'not blank' char is not the expected separator
					// then consider it a script formatting error and abort
					if( i < _uiScriptSize &&_cEntriesSeparator != _strScript[ i ] )
					{
						bSuccess = false;
						break;
					}
				}
			}
			//skip the entries separator
			++i;
		}

		#undef GO_FORWARD_IF
		#undef SKIP_BLANK
		#undef SKIP_ENTRY
		#undef REACH_KEY_END
		#undef REACH_VALUE_END

		return bSuccess;  
	} 
}